package ar.com.spsolutions.splibrary.services.dto;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Transactional;

import ar.com.spsolutions.splibrary.entity.Entity;
import ar.com.spsolutions.splibrary.services.LibraryServiceFacade;

/**
 * @Author leezsi
 */
public abstract class AbstractCRUDDTO<E extends Entity> implements Serializable {
	protected static final Logger LOG = Logger.getLogger(AbstractCRUDDTO.class);
	private static final long serialVersionUID = 5193328342726807735L;
	protected static final String UNCHECKED = "unchecked";
	protected static final String RAWTYPES = "rawtypes";
	private E primaryKey;
	private E model;
	private String searchValue;
	private Boolean newEntity;
	private LibraryServiceFacade libraryServiceFacade;

	@Transactional(readOnly = true)
	protected <T> T getPropertyValue(final String property) {
		final Object anObject = this.getModel();
		if (anObject == null) {
			return null;
		}
		try {
			final Field field = anObject.getClass().getDeclaredField(property);
			final boolean isAccessible = field.isAccessible();
			field.setAccessible(true);
			@SuppressWarnings("unchecked")
			final T result = (T) field.get(anObject);
			field.setAccessible(isAccessible);
			return result;
		} catch (final NoSuchFieldException e) {
			return null;
		} catch (final IllegalAccessException e) {
			return null;
		}
	}

	@Transactional
	protected void setPropertyValue(final String property, final Object value) {
		final Entity anObject = this.getModel();
		if (anObject == null) {
			return;
		}
		try {
			final Field field = anObject.getClass().getDeclaredField(property);
			final boolean isAccessible = field.isAccessible();
			field.setAccessible(true);
			field.set(anObject, value);
			field.setAccessible(isAccessible);
		} catch (final IllegalAccessException e) {
			AbstractCRUDDTO.LOG.warn("Acceso ilegal al field " + property);
		} catch (final SecurityException e) {
			AbstractCRUDDTO.LOG.warn("Problemas de seguridad en el field "
					+ property);
		} catch (final NoSuchFieldException e) {
			AbstractCRUDDTO.LOG.warn("No existe el field " + property);
		}
	}

	public void setPrimaryKey(final E value) {
		this.primaryKey = value;
	}

	protected E getPrimaryKey() {
		return this.primaryKey;
	}

	public void setSearchValue(final String value) {
		this.searchValue = value;
	}

	protected String getSearchValue() {
		return this.searchValue;
	}

	@Transactional(readOnly = true)
	public abstract int allResultsSize();

	public Iterator<? extends E> getResults(final int first, final int count) {
		return this.getResults(this.getSearchValue(), first, count);
	}

	@Transactional
	public abstract Iterator<? extends E> getResults(String searchValue,
			int first, int count);

	public E getModel() {
		return this.model;
	}

	public void setModel(final E model) {
		this.model = model;
	}

	public abstract String getRenderPropertyFor(String id);

	public void forUpdate() {
		this.newEntity = false;
	}

	public void forCreate(final String primaryKeyString) {
		this.newEntity = true;
		final E anEntity = this.getNewEntity();
		this.setModel(anEntity);
		this.setPrimaryKeyInNewEntity(anEntity, primaryKeyString);

	}

	protected abstract E getNewEntity();

	public Boolean isNewEntity() {
		return this.newEntity;
	}

	public void commit() {
		if (this.isNewEntity()) {
			this.save(this.getModel());
		} else {
			this.update(this.getModel());
		}

	}

	protected abstract void setPrimaryKeyInNewEntity(E entity, String primaryKey);

	public void reset() {
		this.setModel(null);
	}

	@Transactional
	protected abstract void update(E entity);

	@Transactional
	protected abstract void save(E entity);

	final public void delete() {
		this.delete(this.getModel());
	}

	@Transactional
	public abstract void delete(E entity);

	public void setNewEntity(final Boolean newEntity) {
		this.newEntity = newEntity;
	}

	public LibraryServiceFacade getLibraryServiceFacade() {
		return this.libraryServiceFacade;
	}

	public void setLibraryServiceFacade(
			final LibraryServiceFacade libraryServiceFacade) {
		this.libraryServiceFacade = libraryServiceFacade;
	}

}
